﻿using System;
using System.Linq;
using SubsidiosBussinessLogic.DataAccess.Interfaces;

namespace SubsidiosBussinessLogic.DataAccess.SqlServerRepository
{
    /// <summary>
    /// Generic repository 
    /// </summary>
    /// <typeparam name="T">Generic type to be used with the repository</typeparam>
    public class Repository<T> where T : class
    {
        protected IDataContextFactory DataContextFactory { get; set; }

        /// <summary>
        /// Return all instances of type T.
        /// </summary>
        /// <returns></returns>
        public virtual IQueryable<T> All()
        {
            return GetTable;
        }

        /// <summary>
        /// Return all instances of type T that match the expression exp.
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        public virtual IQueryable<T> FindAll(Func<T, bool> exp)
        {
            return GetTable.Where<T>(exp).AsQueryable<T>();
        }

        /// <summary>See IRepository.</summary>
        /// <param name="exp"></param><returns></returns>
        public virtual T Single(Func<T, bool> exp)
        {
            return GetTable.Single(exp);
        }

        /// <summary>See IRepository.</summary>
        /// <param name="exp"></param><returns></returns>
        public virtual T First(Func<T, bool> exp)
        {
            return GetTable.First(exp);
        }

        /// <summary>See IRepository.</summary>
        /// <param name="entity"></param>
        public virtual void MarkForDeletion(T entity)
        {
            this.DataContextFactory.Context.GetTable<T>().DeleteOnSubmit(entity);
        }

        /// <summary>
        /// Create a new instance of type T.
        /// And sets ready to insert on the Database
        /// </summary>
        /// <returns>The object created</returns>
        public virtual T CreateInstance()
        {
            T entity = Activator.CreateInstance<T>();
            GetTable.InsertOnSubmit(entity);
            return entity;
        }

        /// <summary>
        /// Inserts a entity in the database. Changes are commited when SaveAll() is called.
        /// </summary>
        /// <param name="entity">The object to insert</param>
        public virtual void Insert(T entity)
        {
            GetTable.InsertOnSubmit(entity);
        }

        /// <summary>See IRepository.</summary>
        public virtual void SaveAll()
        {
            this.DataContextFactory.SaveAll();
        }

        public Repository(IDataContextFactory dataContextFactory)
        {
            this.DataContextFactory = dataContextFactory;
        }

        #region Properties

        private string PrimaryKeyName
        {
            get { return TableMetadata.RowType.IdentityMembers[0].Name; }
        }

        private System.Data.Linq.Table<T> GetTable
        {
            get { return this.DataContextFactory.Context.GetTable<T>(); }
        }

        private System.Data.Linq.Mapping.MetaTable TableMetadata
        {
            get { return this.DataContextFactory.Context.Mapping.GetTable(typeof(T)); }
        }

        private System.Data.Linq.Mapping.MetaType ClassMetadata
        {
            get { return this.DataContextFactory.Context.Mapping.GetMetaType(typeof(T)); }
        }

        #endregion
    }
}